package model

import (
	"cgs-server/helper"
	"cgs-server/pkg/set"
	"errors"
	"go.mongodb.org/mongo-driver/bson/primitive"
)

// User is a user's model.
// 之前返回前端的字段都是大写开头，现在改为小写
type User struct {
	// ID
	//UserId string `json:"userId" bson:"-"`
	UserId primitive.ObjectID `json:"userId" bson:"ID"`
	// Username
	Username string `json:"username" bson:"Username" validate:"required"`
	// Password (md5 + Salt)
	Password string `json:"password,omitempty" bson:"-"`
	Pwd      string `json:"-" bson:"Password"`
	// Name
	Name string `json:"name" bson:"Name"`
	// Role ID
	RoleID string `json:"roleId" bson:"RoleID"`
	// Role Name (won't store in the db)
	RoleName string `json:"roleName" bson:"-"`
	// Department ID
	DeptID string `json:"deptId" bson:"DeptId"`
	Depts  string `json:"depts" bson:"Depts"`
	// Department Name (won't store in the db)
	DeptName string `json:"-" bson:"-"`
	// Gender: 0-unset, 1-male, 2-female
	Gender int `json:"gender" bson:"Gender"`
	// Phone Number
	Phone string `json:"phone,omitempty" bson:"Phone"`
	// Emal
	Email string `json:"email,omitempty" bson:"Email"`
	// QQ
	QQ string `json:"qq,omitempty" bson:"QQ"`
	// Password Salt
	Salt string `json:"-" bson:"Salt"`
	// all operating authorities (won't store in the db)
	OperatingAuthorities []string `json:"operatingAuthorities" bson:"-"`

	TenantModel `json:",inline" bson:",inline"`

	// 2020-12-18, 目前弃用 Roles 字段，用户和角色处理成一对多关系
	Roles       *Roles `json:"roles,omitempty" bson:"-"`       // 角色列表
	Authorities string `json:"authorities,omitempty" bson:"-"` // 权限列表

	Role *Role `json:"role,omitempty" bson:"-"`

	// 应用角色的权限
	AppAuths        map[string][]string            `json:"appAuths" bson:"-"`
	FormAuths       map[string]map[string][]string `json:"formAuths" bson:"-"`
	AppAuthSet      map[string]set.Set             `json:"-" bson:"-"`
	ProjVisualIdMap map[string]string              `json:"projVisualIdMap" bson:"-"`
	SceneProjIdMap  map[string]string              `json:"sceneProjIdMap" bson:"-"`

	// 应用角色和VisualId 映射表，用来查询权限
	VisualAppMap map[string][]primitive.ObjectID `json:"-" bson:"-"`
	// 每个应用的表单权限类型
	AppFormAuthType map[string]int `json:"appFormAuthType" bson:"-"`
}

//type FormAuths map[string]map[string][]string

func (u *User) HasAuth(visualId string, authName ...interface{}) bool {
	if u.AppAuthSet == nil || u.AppAuthSet[visualId] == nil {
		return false
	}
	return u.AppAuthSet[visualId].Contains(authName...)
}
func (u *User) HasOneAuth(visualId string, authName ...interface{}) bool {
	if u.AppAuthSet == nil || u.AppAuthSet[visualId] == nil {
		return false
	}
	return u.AppAuthSet[visualId].ContainsOne(authName...)
}

func (u *User) IsFormAuthTypeDistributeAll(visualId string) bool {
	if u.AppFormAuthType == nil || u.AppFormAuthType[visualId] == 0 {
		return true
	}
	return false
}

func (u *User) HasFormAuth(visualId string, formId string, authName ...interface{}) bool {
	// 需要判断表单的权限类型
	if u.IsFormAuthTypeDistributeAll(visualId) {
		return true
	}
	if u.FormAuths == nil && u.FormAuths[visualId] == nil {
		return true
	}
	if len(u.FormAuths[visualId][formId]) == 0 {
		return false
	}
	authSet := set.NewSet()
	for _, s := range u.FormAuths[visualId][formId] {
		authSet.Add(s)
	}
	return authSet.Contains(authName...)
}

func (u *User) HasOneFormAuth(visualId string, formId string, authName ...interface{}) bool {
	// 需要判断表单的权限类型
	if u.IsFormAuthTypeDistributeAll(visualId) {
		return true
	}
	if u.FormAuths == nil && u.FormAuths[visualId] == nil {
		return true
	}
	if len(u.FormAuths[visualId][formId]) == 0 {
		return false
	}
	authSet := set.NewSet()
	for _, s := range u.FormAuths[visualId][formId] {
		authSet.Add(s)
	}
	return authSet.ContainsOne(authName...)
}

type Users []*User

func (u *User) GetId() string {
	return u.UserId.Hex()
}

func (u *User) SetId(id primitive.ObjectID) {
	u.UserId = id
}

func (u *User) Compare(password string) error {
	if u.Username != helper.MD5(password+u.Salt) {
		return errors.New("")
	}
	return nil
}

// 根据 sceneId 判断是否有编辑场景的权限
func (u *User) HasEditSceneAuth(sceneId string) bool {
	if u.SceneProjIdMap == nil || u.ProjVisualIdMap == nil || u.AppAuths == nil {
		return false
	}
	if projId, ok := u.SceneProjIdMap[sceneId]; ok {
		if visualId, ok := u.ProjVisualIdMap[projId]; ok {
			if auths, ok := u.AppAuths[visualId]; ok {
				if len(auths) > 0 {
					for i := range auths {
						if auths[i] == "EDIT_SCENE" {
							return true
						}
					}
				}
			}
		}
	}
	return false
}

// HasVisualAuth 根据 visualId 判断是否有这个应用的权限
func (u *User) HasVisualAuth(visualId string) bool {
	if u.RoleName == "AppAdministrator" || u.RoleName == "Administrator" {
		return true
	} else {
		if len(u.AppAuths) == 0 {
			// 没有任何应用，即表示不拥有任何表单
			return false
		}
		if visualId != "" {
			// 若 visualId 不为空，判断用户是否有这个应用
			if _, ok := u.AppAuths[visualId]; ok {
				return true
			}
			//for k := range u.AppAuths {
			//	if visualId == k {
			//		// 用户有这个应用
			//		return true
			//	}
			//}
		}
	}
	return false
}

type UserInfo struct {
	User
	Authority map[string]bool `json:"authority" bson:"authority"`
}

type ChangeUserPasswordModel struct {
	User            `validate:"-"`
	OldPassword     string `json:"oldPassword" validate:"omitempty"`
	NewPassword     string `json:"newPassword" validate:"required,eqfield=ConfirmPassword"`
	ConfirmPassword string `json:"confirmPassword" validate:"required"`
	PhoneNumber     string `json:"phoneNumber" validate:"omitempty,phone"`
	ValidationCode  string `json:"validationCode" validate:"required_with=PhoneNumber,omitempty,len=6"`
}

//HasRole 是否是某个角色
func (u *User) HasRole(roleAlias string) bool {
	if u.Role == nil {
		return u.RoleName == roleAlias
	}
	return u.Role.RoleAlias == roleAlias
}

//IsAdmin 是否是管理员角色
func (u *User) IsAdmin() bool {
	return u.HasRole("Administrator")
}

//IsSuperAdmin 是否是超级管理员
func (u *User) IsSuperAdmin() bool {
	return u.TenantId == "000000" && u.IsAdmin()
}

//IsAppAdmin 是否是应用管理员角色
func (u *User) IsAppAdmin() bool {
	return u.HasRole("Administrator") || u.HasRole("AppAdministrator")
}

// 用户字段校验
func (u *User) ValidatePhoneQQEmail() error {
	if u.Phone != "" && !helper.CheckPhone(u.Phone) {
		return errors.New("Wrong phone number.")
	}
	if u.Email != "" && !helper.CheckEmail(u.Email) {
		return errors.New("Wrong email address.")
	}
	if u.QQ != "" && !helper.CheckQQ(u.QQ) {
		return errors.New("Wrong qq number.")
	}
	return nil
}

type AppAuthVo struct {
	AppAuth  []string            `json:"appAuth"`
	FormAuth map[string][]string `json:"formAuth"`
}
